package com.senseauto.ah8mediacodec.muxer;


import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import androidx.annotation.NonNull;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;
import java.util.Vector;

/**
 * 音视频混合线程
 */
public class MediaMuxerThread extends Thread {

    private static final String TAG = "MediaMuxerThread";

    public static final int TRACK_VIDEO = 0;
    public static final int TRACK_AUDIO = 1;

    private final Object lock = new Object();

    private static MediaMuxerThread mediaMuxerThread;

    private AudioEncoderThread audioThread;
    private VideoEncoderThread videoThread;

    private MediaMuxer mediaMuxer;
    private Vector<MuxerData> muxerDatas;

    private int videoTrackIndex = -1;
    private int audioTrackIndex = -1;

    //private static FileUtils fileSwapHelper;

    // 音轨添加状态
    private volatile boolean isVideoTrackAdd;
    private volatile boolean isAudioTrackAdd;

    private volatile boolean isExit = false;

    private static int mWidth, mHeight;

    private static String saveVideoPath;

    //2024-4-12 这三个编码线程换成HandlerThread的写法，cpu占用可以降低60%
    //子线程的looper，模拟HandlerThread里实现
    private Looper mLooper;
    private Handler handler; //处理消息的子线程的handler

    private int MSG_DATA_ADDED = 102;
    private int MSG_CHECK_TRACK_ADDED = 104;

    private MediaMuxerThread() {
        super("MediaMuxerThread");//给线程取个名字，方便调试cpu占用看哪个线程
        // 构造函数
        Log.e(TAG, "构造函数MediaMuxerThread()");
    }

    //步骤0、外部需要手动调用的方法，设置视频录制大小尺寸(录制时视频的大小可能和camera预览大小不一致哦)
    public static void init(int width, int height) {
        mWidth = width;
        mHeight = height;
    }

    //步骤0、外部需要手动调用的方法，设置视频保存全路径+文件名称
    public static void setVideoName(String videoName) {
        saveVideoPath = videoName;
    }

    //步骤1、这个方法调用后开始编码音视频了
    public static void startMuxer() {
        Log.e(TAG, "startMuxer()");
        if (mediaMuxerThread == null) {
            synchronized (MediaMuxerThread.class) {
                if (mediaMuxerThread == null) {
                    mediaMuxerThread = new MediaMuxerThread();
                }
                mediaMuxerThread.start();
            }
        }
    }

    //步骤4、对外提供的方法，退出时候，停止录制的时候调用
    public static void stopMuxer() {
        Log.e(TAG, "stopMuxer()");
        if (mediaMuxerThread != null) {
            mediaMuxerThread.exit();
            try {
                mediaMuxerThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.e(TAG, "mediaMuxerThread != null and set it null");
            mediaMuxerThread = null;
        }

    }

    private void readyStart() throws IOException {
        /*fileSwapHelper.requestSwapFile(true);
        String fileName = fileSwapHelper.getNextFileName();*/
        Log.e(TAG, "readyStart=" + saveVideoPath);
        readyStart(saveVideoPath);
    }

    //内部调用的方法
    private void readyStart(String filePath) throws IOException {
        isExit = false;
        isVideoTrackAdd = false;
        isAudioTrackAdd = false;
        muxerDatas.clear();
        Log.e(TAG, "readyStart(String filePath) new MediaMuxer(filePath---> path=" + filePath);
        mediaMuxer = new MediaMuxer(filePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
        if (audioThread != null) {
            audioThread.setMuxerReady(true);
        }
        if (videoThread != null) {
            videoThread.setMuxerReady(true);
        }
    }

    //MediaMuxerThread线程启动后调用下面方法
    //步骤 3、视频录制对外提供的方法， 添加视频数据，在摄像头数据处理好后调用
    public static void addVideoFrameData(byte[] data) {
        if (mediaMuxerThread != null) {
            mediaMuxerThread.addVideoData(data);
        }
    }

    //3.2、给音频录制调用的方法，不需要手动调用，AudioEncoderThread里封装好了
    public void addMuxerData(MuxerData data) {
        if (!isMuxerStart()) {
            return;
        }
        muxerDatas.add(data);
        handler.sendEmptyMessage(MSG_DATA_ADDED);
    }

    /**
     * 3.1、第一个数据来的时候，会先添加音轨，被动调用，在子线程VideoEncoderThread 和 AudioEncoderThread里 第一次拿到编码完成后的数据后，子线程调用通知他
     * 添加视频／音频轨
     * @param index
     * @param mediaFormat
     */
    public synchronized void addTrackIndex(int index, MediaFormat mediaFormat) {
        if (isMuxerStart()) {
            return;
        }

        /* 如果已经添加了，就不做处理了 */
        if ((index == TRACK_AUDIO && isAudioTrackAdd()) || (index == TRACK_VIDEO && isVideoTrackAdd())) {
            Log.e(TAG, "addTrackIndex 两个轨道都有，return");
            return;
        }

        if (mediaMuxer != null) {
            int track = 0;
            try {
                track = mediaMuxer.addTrack(mediaFormat);
            } catch (Exception e) {
                Log.e(TAG, "addTrack 异常:" + e.toString());
                return;
            }

            if (index == TRACK_VIDEO) {
                videoTrackIndex = track;
                isVideoTrackAdd = true;
                handler.sendEmptyMessage(MSG_CHECK_TRACK_ADDED);
                Log.e(TAG, "添加视频轨完成");
            } else {
                audioTrackIndex = track;
                isAudioTrackAdd = true;
                handler.sendEmptyMessage(MSG_CHECK_TRACK_ADDED);
                Log.e(TAG, "添加音轨完成");
            }
        }
    }

    /**
     * 请求混合器开始启动
     */
    private void requestStart() {
        synchronized (lock) {
            if (isMuxerStart()) {
                mediaMuxer.start();
                lock.notify();
            }
        }
    }

    /**
     * 当前是否添加了音轨
     *
     * @return
     */
    public boolean isAudioTrackAdd() {
        return isAudioTrackAdd;
    }

    /**
     * 当前是否添加了视频轨
     *
     * @return
     */
    public boolean isVideoTrackAdd() {
        return isVideoTrackAdd;
    }

    /**
     * 当前音视频合成器是否运行了
     *
     * @return
     */
    public boolean isMuxerStart() {
        return isAudioTrackAdd && isVideoTrackAdd;
    }


    private void addVideoData(byte[] data) {
        if (videoThread != null) {
            videoThread.add(data);
        }
    }

    //内部调用的方法，在run()里调用，分别编码是视频和音频的
    private void initMuxer() {
        Log.e(TAG, "initMuxer: ");
        muxerDatas = new Vector<>();
        //fileSwapHelper = new FileUtils();
        audioThread = new AudioEncoderThread((new WeakReference<MediaMuxerThread>(this)));
        videoThread = new VideoEncoderThread(mWidth, mHeight, new WeakReference<MediaMuxerThread>(this));
        audioThread.start();
        videoThread.start();
        try {
            readyStart();
        } catch (IOException e) {
            Log.e(TAG, "initMuxer 异常:" + e.toString());
        }
    }

    //混合器调用不同的方法时，通过handler发消息执行对应的方法
    private void initHandler(){
        handler = new Handler(mLooper){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                if(msg.what == MSG_DATA_ADDED){ //有数据添加进来了
                    if(!muxerDatas.isEmpty()){
                        MuxerData data = muxerDatas.remove(0);
                        int track;
                        if (data.trackIndex == TRACK_VIDEO) {
                            track = videoTrackIndex;
                        } else {
                            track = audioTrackIndex;
                        }
                        try {
                            mediaMuxer.writeSampleData(track, data.byteBuf, data.bufferInfo);
                        } catch (Exception e) {
                            Log.e(TAG, "写入混合数据失败!" + e.toString());
                        }
                        sendEmptyMessage(MSG_DATA_ADDED);
                    }else{
                        //500ms后再看是否有数据
                        sendEmptyMessageDelayed(MSG_DATA_ADDED,500);
                    }
                }else if(msg.what == MSG_CHECK_TRACK_ADDED){ //有一个音轨或者视频轨道添加进混合器了
                    Log.e(TAG, "检查两个规定是否都添加了，添加了就可以加数据编码混合了");
                    requestStart();
                }
            }
        };
    }

    @Override
    public void run() {
        super.run();
        Log.e(TAG, "thread run()-->initMuxer();");
        // 初始化混合器
        initMuxer();

        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();    //唤醒其他线程的等待锁，这边是为了唤醒getLooper()方法中的等待锁
        }
        initHandler();//准备子线程的handler，初始化好handler接收消息处理调用对应的方法，消息的发送，会在音频和视频数据添加到集合里编码后发出
        Looper.loop();

        /*while (!isExit) {
            if (isMuxerStart()) {
                if (muxerDatas.isEmpty()) {
                    synchronized (lock) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                } else {
                    MuxerData data = muxerDatas.remove(0);
                    int track;
                    if (data.trackIndex == TRACK_VIDEO) {
                        track = videoTrackIndex;
                    } else {
                        track = audioTrackIndex;
                    }
                    try {
                        mediaMuxer.writeSampleData(track, data.byteBuf, data.bufferInfo);
                    } catch (Exception e) {
                        Log.e(TAG, "写入混合数据失败!" + e.toString());
                    }

                }
            } else {
                synchronized (lock) {
                    try {
                        Log.e(TAG, "等待音视轨添加...");
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        Log.e(TAG, "addTrack 异常:" + e.toString());
                    }
                }
            }
        }*/
        //readyStop();
//        Log.e(TAG, "混合器退出...");
    }

    private void restart() {
        restart(saveVideoPath);
    }

    private void restart(String filePath) {
        restartAudioVideo();
        readyStop();

        try {
            readyStart(filePath);
        } catch (Exception e) {
            Log.e(TAG, "readyStart(filePath, true) " + "重启混合器失败 尝试再次重启!" + e.toString());
            restart();
            return;
        }
//        Log.e(TAG, "重启混合器完成");
    }


    private void readyStop() {
        if (mediaMuxer != null) {
            try {
                mediaMuxer.stop();
            } catch (Exception e) {
                Log.e(TAG, "mediaMuxer.stop() 异常:" + e.toString());
            }
            try {
                mediaMuxer.release();
            } catch (Exception e) {
                Log.e(TAG, "mediaMuxer.release() 异常:" + e.toString());

            }
            mediaMuxer = null;
        }
    }

    private void restartAudioVideo() {
        if (audioThread != null) {
            audioTrackIndex = -1;
            isAudioTrackAdd = false;
            audioThread.restart();
        }
        if (videoThread != null) {
            videoTrackIndex = -1;
            isVideoTrackAdd = false;
            videoThread.restart();
        }
    }

    private void exit() {
        if (videoThread != null) {
            videoThread.exit();
            try {
                videoThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (audioThread != null) {
            audioThread.exit();
            try {
                audioThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        Log.e(TAG, "exit() 修改isExit = true 退出run里的while循环");
        isExit = true;
        synchronized (lock) {
            lock.notify();
        }
        readyStop();
        mLooper.quitSafely();
    }

    /**
     * 封装需要传输的数据类型
     */
    public static class MuxerData {

        int trackIndex;
        ByteBuffer byteBuf;
        MediaCodec.BufferInfo bufferInfo;

        public MuxerData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo) {
            this.trackIndex = trackIndex;
            this.byteBuf = byteBuf;
            this.bufferInfo = bufferInfo;
        }
    }


}
